package com.skyline.wizard.searcher.impl;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import net.paoding.analysis.analyzer.PaodingAnalyzer;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.RangeQuery;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;

import com.skyline.common.bean.Page;
import com.skyline.wizard.mapper.ObjMapper;
import com.skyline.wizard.searcher.Searcher;

/**
 * Class <code>SearcherImpl</code> SearcherImpl是对Searcher的实现
 * 
 * @author Jairus Chan
 * @version 0.1, 11/30/10
 * @since JDK1.6
 */

public class BaseSearcherImpl<T> implements Searcher<T> {

	private Directory directory;
	private Analyzer defaultAnalyzer = null;
	private Analyzer standardAnalyzer = null;
	private IndexSearcher indexSearcher = null;
	private ObjMapper<T> mapper = null;
	private String[] fields = null;

	private Integer maxDocNum = 1000;

	@SuppressWarnings("deprecation")
	private void intialAnalyzer() {
		if (defaultAnalyzer == null) {
			defaultAnalyzer = new PaodingAnalyzer();
		}
		if (standardAnalyzer == null) {
			standardAnalyzer = new StandardAnalyzer();
		}
	}

	public void inital(String indexPath, ObjMapper<T> mapper, String[] fields) {
		try {
			//System.out.println(indexPath);
			directory = FSDirectory.open(new File(indexPath));
			this.mapper = mapper;
			this.fields = fields;
			//this.indexSearcher = new IndexSearcher(directory);
			this.rebuildSearcher();
			intialAnalyzer();

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public List<T> searchWithDefaultAnalyzer(String queryString, Page page) {
		return search(queryString, page, defaultAnalyzer);
	}

	@Override
	public List<T> searchWithStandardAnalyzer(String queryString, Page page) {
		return search(queryString, page, standardAnalyzer);

	}

	/*
	 * public List<T> booleanSearchWithDefaultAnalyzer(String[] mustTermKeys,
	 * String[] mustTermValues, String[] mustNotTermKeys, String[]
	 * mustNotTermValues, String[] rangeKeys, String[] rangeBeginValues,
	 * String[] rangeEndValues, Page page) { return booleanSearch(mustTermKeys,
	 * mustTermValues, mustNotTermKeys, mustNotTermValues, rangeKeys,
	 * rangeBeginValues, rangeEndValues, page, defaultAnalyzer); }
	 * 
	 * public List<T> booleanSearchWithStandardAnalyzer(String[] mustTermKeys,
	 * String[] mustTermValues, String[] mustNotTermKeys, String[]
	 * mustNotTermValues, String[] rangeKeys, String[] rangeBeginValues,
	 * String[] rangeEndValues, Page page) { return booleanSearch(mustTermKeys,
	 * mustTermValues, mustNotTermKeys, mustNotTermValues, rangeKeys,
	 * rangeBeginValues, rangeEndValues, page, standardAnalyzer);
	 * 
	 * }
	 */
	protected List<T> booleanSearchWithDefaultAnalyzer(List<String> shouldTermKeys, List<String> shouldTermValues,
			List<String> mustTermKeys, List<String> mustTermValues, List<String> mustNotTermKeys, List<String> mustNotTermValues,
			List<String> rangeKeys, List<String> rangeBeginValues, List<String> rangeEndValues, Page page) {
		return booleanSearch(shouldTermKeys, shouldTermValues, mustTermKeys, mustTermValues, mustNotTermKeys,
				mustNotTermValues, rangeKeys, rangeBeginValues, rangeEndValues, page, defaultAnalyzer);
	}

	protected List<T> booleanSearchWithStandardAnalyzer(List<String> shouldTermKeys, List<String> shouldTermValues,
			List<String> mustTermKeys, List<String> mustTermValues, List<String> mustNotTermKeys, List<String> mustNotTermValues,
			List<String> rangeKeys, List<String> rangeBeginValues, List<String> rangeEndValues, Page page) {
		return booleanSearch(shouldTermKeys, shouldTermValues, mustTermKeys, mustTermValues, mustNotTermKeys,
				mustNotTermValues, rangeKeys, rangeBeginValues, rangeEndValues, page, standardAnalyzer);
	}

	@SuppressWarnings("deprecation")
	protected List<T> booleanSearch(List<String> shouldTermKeys, List<String> shouldTermValues, List<String> mustTermKeys,
			List<String> mustTermValues, List<String> mustNotTermKeys, List<String> mustNotTermValues, List<String> rangeKeys,
			List<String> rangeBeginValues, List<String> rangeEndValues, Page page, Analyzer analyzer) {
		this.rebuildSearcher();
		try {
			BooleanQuery booleanQuery = new BooleanQuery();

			if (shouldTermKeys.size() == shouldTermValues.size()) {
				for (int i = 0; i < shouldTermKeys.size(); i++) {
					Term t = new Term(shouldTermKeys.get(i), shouldTermValues.get(i));
					TermQuery q = new TermQuery(t);
					booleanQuery.add(q, BooleanClause.Occur.SHOULD);
				}
			}
			if (mustTermKeys.size() == mustTermValues.size()) {
				for (int i = 0; i < mustTermKeys.size(); i++) {
					if (mustTermValues.get(i) != null) {
						Term t = new Term(mustTermKeys.get(i), mustTermValues.get(i));
						TermQuery q = new TermQuery(t);
						booleanQuery.add(q, BooleanClause.Occur.MUST);
					}
				}
			}
			if (mustNotTermKeys.size() == mustNotTermValues.size()) {
				for (int i = 0; i < mustNotTermKeys.size(); i++) {
					if (mustNotTermValues.get(i) != null) {
						Term t = new Term(mustNotTermKeys.get(i), mustNotTermValues.get(i));
						TermQuery q = new TermQuery(t);
						booleanQuery.add(q, BooleanClause.Occur.MUST_NOT);
					}
				}
			}
			if (rangeKeys.size() != 0 && rangeKeys.size() == rangeBeginValues.size()
					&& rangeKeys.size() == rangeEndValues.size()) {
				for (int i = 0; i < rangeKeys.size(); i++) {
					Query query = new RangeQuery(new Term(rangeKeys.get(i), rangeBeginValues.get(i)), new Term(rangeKeys.get(i),
							rangeEndValues.get(i)), true);
					booleanQuery.add(query, BooleanClause.Occur.MUST);
				}
			}

			int firstResult = (page.getCurpage() - 1) * page.getSize();
			int maxResult = page.getSize();

			List<T> documents = new ArrayList<T>();

			TopDocs topDocs = indexSearcher.search(booleanQuery, null, maxDocNum);

			int total = topDocs.totalHits;
			page.setTotal(total);
			int end = Math.min(firstResult + maxResult, total);
			for (int i = firstResult; i < end; i++) {
				ScoreDoc scoreDoc = topDocs.scoreDocs[i];
				int docSn = scoreDoc.doc;
				T doc = (T) mapper.map(indexSearcher.doc(docSn));
				documents.add(doc);
			}

			return documents;
		} catch (Exception e) {
			return null;
		}
	}

	@SuppressWarnings("deprecation")
	protected List<T> search(String queryString, Page page, Analyzer analyzer) {
		this.rebuildSearcher();
		try {
			//System.out.println(queryString);
			// indexSearcher = new IndexSearcher(directory);

			int firstResult = (page.getCurpage() - 1) * page.getSize();
			int maxResult = page.getSize();
			// List<Document> documents = new ArrayList<Document>();

			List<T> documents = new ArrayList<T>();

			QueryParser queryParser = new MultiFieldQueryParser(fields, analyzer);
			Query query = queryParser.parse(queryString);

			// Filter filter = null;
			// Sort sort = new Sort();
			// sort.setSort("id", true);
			TopDocs topDocs = indexSearcher.search(query, null, maxDocNum);

			int total = topDocs.totalHits;
			int end = Math.min(firstResult + maxResult, total);
			for (int i = firstResult; i < end; i++) {
				ScoreDoc scoreDoc = topDocs.scoreDocs[i];
				int docSn = scoreDoc.doc;
				T doc = (T) mapper.map(indexSearcher.doc(docSn));
				documents.add(doc);
			}
			
			page.setTotal(total);
//			int totalPage=0;
//			page.set(totalPage);

			return documents;
		} catch (Exception e) {
			return null;
		}
	}
	
	@SuppressWarnings("deprecation")
	private void rebuildSearcher(){
		try {
			this.indexSearcher = new IndexSearcher(directory);
		} catch (CorruptIndexException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
